package com.my.commons.database.dao;



import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.my.commons.database.DatabaseConfig;
import com.my.commons.scripting.scriptmanager.ScriptManager;

import com.my.commons.database.DatabaseFactory;

public class DAOManager {
	/**
     * Logger for DAOManager class
     */
    private static final Logger log = Logger.getLogger(DAOManager.class);

    /**
     * Collection of registered DAOs
     */
    private static final Map<String, DAO> daoMap = new HashMap<String, DAO>();

    /**
     * This script manager is responsible for loading {@link com.aionemu.commons.database.dao.DAO} implementations
     */
    private static ScriptManager scriptManager;

    /**
     * Initializes DAOManager.
     */
    public static void init() {
        try {
            scriptManager = new ScriptManager();
            scriptManager.setGlobalClassListener(new DAOLoader());
            scriptManager.load(DatabaseConfig.DATABASE_SCRIPTCONTEXT_DESCRIPTOR);
        }
        catch (Exception e) {
            throw new Error("Can't load database script context: " + DatabaseConfig.DATABASE_SCRIPTCONTEXT_DESCRIPTOR,
                    e);
        }

        log.info("Loaded " + daoMap.size() + " DAO implementations.");
    }

    /**
     * Shutdown DAOManager
     */
    public static void shutdown() {
        scriptManager.shutdown();
        daoMap.clear();
        scriptManager = null;
    }

    /**
     * Returns DAO implementation by DAO class. Typical usage:
     * <p/>
     * <pre>
     * AccountDAO	dao	= DAOManager.getDAO(AccountDAO.class);
     * </pre>
     *
     * @param clazz Abstract DAO class implementation of which was registered
     * @param <T>   Subclass of DAO
     * @return DAO implementation
     * @throws DAONotFoundException if DAO implementation not found
     */
    @SuppressWarnings("unchecked")
    public static <T extends DAO> T getDAO(Class<T> clazz) throws DAONotFoundException {

        DAO result = daoMap.get(clazz.getName());

        if (result == null) {
            String s = "DAO for class " + clazz.getName() + " not implemented";
            log.error(s);
            throw new DAONotFoundException(s);
        }

        return (T) result;
    }

    /**
     * Registers {@link DAO}.<br>
     * First it creates new instance of DAO, then invokes {@link DAO#supports(String, int, int)} <br>
     * . If the result was possitive - it associates DAO instance with
     * {@link com.aionemu.commons.database.dao.DAO#getClassName()} <br>
     * If another DAO was registed - {@link com.aionemu.commons.database.dao.DAOAlreadyRegisteredException} will be
     * thrown
     *
     * @param daoClass DAO implementation
     * @throws DAOAlreadyRegisteredException if DAO is already registered
     * @throws IllegalAccessException        if something went wrong during instantiation of DAO
     * @throws InstantiationException        if something went wrong during instantiation of DAO
     */
    public static void registerDAO(Class<? extends DAO> daoClass) throws DAOAlreadyRegisteredException,
            IllegalAccessException, InstantiationException {
        DAO dao = daoClass.newInstance();

        if (!dao.supports(DatabaseFactory.getDatabaseName(), DatabaseFactory.getDatabaseMajorVersion(), DatabaseFactory.getDatabaseMinorVersion())) {
            return;
        }

        synchronized (DAOManager.class) {
            DAO oldDao = daoMap.get(dao.getClassName());
            if (oldDao != null) {
                StringBuilder sb = new StringBuilder();
                sb.append("DAO with className ").append(dao.getClassName()).append(" is used by ");
                sb.append(oldDao.getClass().getName()).append(". Can't override with ");
                sb.append(daoClass.getName()).append(".");
                String s = sb.toString();
                log.error(s);
                throw new DAOAlreadyRegisteredException(s);
            } else {
                daoMap.put(dao.getClassName(), dao);
            }
        }

        if (log.isDebugEnabled())
            log.debug("DAO " + dao.getClassName() + " was successfuly registered.");
    }

    /**
     * Unregisters DAO class
     *
     * @param daoClass DAO implementation to unregister
     */
    public static void unregisterDAO(Class<? extends DAO> daoClass) {
        synchronized (DAOManager.class) {
            for (DAO dao : daoMap.values()) {
                if (dao.getClass() == daoClass) {
                    daoMap.remove(dao.getClassName());

                    if (log.isDebugEnabled())
                        log.debug("DAO " + dao.getClassName() + " was successfuly unregistered.");

                    break;
                }
            }
        }
    }

    private DAOManager() {
        // empty
    }
}
